package com.fowo.api.sys.performance.writer;

import com.fowo.api.sys.performance.PerformanceLogWriter;
import com.fowo.api.sys.performance.model.PerformanceLogModel;
import com.fowo.api.sys.performance.properties.PerformanceLogProperties;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledFuture;

/**
 * 批次性能日志写入器
 */
public abstract class AbstractBatchPerformanceLogWriter implements PerformanceLogWriter {

    /**
     * 待写队列
     */
    private final Queue<PerformanceLogModel> queues = new ConcurrentLinkedQueue<>();

    /**
     * 定时器线程
     */
    private final ThreadPoolTaskScheduler executor;
    protected final PerformanceLogProperties properties;
    private ScheduledFuture<?> timer;

    public AbstractBatchPerformanceLogWriter(PerformanceLogProperties properties) {
        this.properties = properties;
        ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
        executor.setPoolSize(1);
        executor.setThreadNamePrefix(this.getClass().getSimpleName());
        executor.initialize();
        this.executor = executor;
        resetTimer();
    }

    /**
     * 重置写入定时器
     */
    protected void resetTimer() {
        if (timer != null && !timer.isCancelled() && !timer.isDone()) {
            timer.cancel(false);
        }
        Date now = new Date();
        Date triggerDate = new Date(now.getTime() + properties.getMaxWaitTime() * 1000L);
        this.timer = executor.schedule(this::onTime, triggerDate);
    }

    public void onTime() {
        if (queues.isEmpty()) {
            resetTimer();
            return;
        }
        int size = Math.min(queues.size(), properties.getMaxWriteRecords());
        List<PerformanceLogModel> logs = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            logs.add(queues.remove());
        }
        try {
            batchWrite(logs);
        } catch (Exception ignored) {
        }
        resetTimer();
    }

    @Override
    public void write(PerformanceLogModel log) throws Exception {
        if (properties.getMaxWriteRecords() <= 1 || properties.getMaxWaitTime() <= 0) {
            // 立即处理模式
            batchWrite(Collections.singletonList(log));
        } else {
            queues.add(log);
            if (queues.size() >= properties.getMaxWriteRecords()) {
                onTime();
            }
        }
    }

    @Override
    public void flush() {
        if (queues.isEmpty()) {
            return;
        }
        try {
            List<PerformanceLogModel> all = new ArrayList<>(queues);
            queues.clear();
            batchWrite(all);
        }
        catch (Exception ignored) {}
    }

    private void onShutdown() {
        flush();
    }

    abstract void batchWrite(List<PerformanceLogModel> logs);
}
